/*
Copyright (C) 2021-2025 Casa Xu (also Zhiyan Xu) from HIT

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

/******************TMS570 I2C功能说明*************************
* TMS570使用硬件I2C,许多细节操作由硬件自动执行：例如SCL SDA拉高拉低的具体操作
* 需要特别注意DataCount的设置问题，在HALCoGen中可以设置该值，在i2cInit()函数中 i2cREG1->CNT = 5U 处实现
* 该值可以根据需要通过i2cSetCount()在发送/接收前进行更改
* DataCount（寄存器：I2CCNT）是一个高优先级计数器，每发送/接收一个字节后该值都会减1，当该值为0时才能发出stop，否则即使用i2cSetStop()也无法停止
* 该值清0后还会自动发出stop，在某些情况下会造成不小的麻烦：比如需要在一次传输中先写再读，中间不能出现stop
* 将I2CMDR的第7位（RM）置1可以屏蔽DataCount，从而可以按照自己的需求随时使用i2cSetStop()产生停止条件
* 该操作可以通过使能HALCoGen中的EnableRepeatMode来实现，也可以通过写寄存器的值实现
* 重复模式中，在不超时的情况下，需要调用函数发出stop信号，若超时，系统会自动发送stop条件关闭总线，非重复模式则没有超时停止特性。
*/

#include "LOS_I2C.h"


/** @fn void LOS_I2C_Init(void)
*   @brief I2C初始化函数
*/
void LOS_I2C_Init(void)
{
    i2cInit();
}

/** @fn void LOS_I2C_Transmit_M(uint8_t I2C_NUM, uint32_t length, uint8_t * data, uint32_t sadd)
*   @brief IIC Send Data(Master)
*   @param[in] I2C_NUM    - 要使用的I2C端口号（1 2 3 4 ……）
*   @param[in] length - 要发送的数据长度（字节）
*   @param[in] data   - 指向要传送数据的指针
*   @param[in] sadd   - 目的地址
*   @note 该函数先设置从机地址、传输方向、主机模式，再发出开始信号，随后开始发送数据，发送完毕后发出结束信号
*         该函数只支持单纯发送的情况，不支持中途切换收发模式
*/
void LOS_I2C_Transmit_M(uint8_t I2C_NUM, uint32_t length, uint8_t * data, uint32_t sadd)
{
    i2cBASE_t * i2cREGx=i2cREG1;
    
    if (I2C_NUM==1)
    {
        i2cREGx=i2cREG1;
    }
    else if (I2C_NUM==2)
    {
        i2cREGx=i2cREG2;
    }
    
    i2cSetSlaveAdd(i2cREGx, sadd);//设置从机地址
    i2cSetDirection(i2cREGx, I2C_TRANSMITTER);//设置传输方向：发送
    i2cSetCount(i2cREGx, length); //决定了发送多少个字节后发出STOP，这句必须有，只有count计数到0了，start和stop才能正常发出，否则会被阻塞
    i2cSetMode(i2cREGx, I2C_MASTER);//设置模式：主机
    i2cSetStop(i2cREGx);//确保总线处于关闭状态
    i2cSetStart(i2cREGx);//发送开始信号
    i2cSend(i2cREGx, length, data);//发送，若有NACK，会卡在该函数中
    i2cSetStop(i2cREGx);
    while(i2cIsBusBusy(i2cREGx) == true);
    while(i2cIsStopDetected(i2cREGx) == 0);
    i2cClearSCD(i2cREGx);//清除寄存器中的停止检测标志位
}


/** @fn void LOS_I2C_Receive_M(uint8_t I2C_NUM, uint32_t length, uint8_t * data, uint32_t sadd)
*   @brief IIC Send Data(Master)
*   @param[in] I2C_NUM    - 要使用的I2C端口号（1 2 3 4 ……）
*   @param[in] length - 要接收的数据长度（字节）
*   @param[in] data   - 指向存储接收数据的指针
*   @param[in] sadd   - 从机地址
*   @note 该函数先设置从机地址、传输方向、主机模式，再发出开始信号，随后开始接收从机数据，接收指定长度数据后发出结束信号
*         该函数只支持单纯接收的情况，不支持中途切换收发模式
*/
void LOS_I2C_Receive_M(uint8_t I2C_NUM, uint32_t length, uint8_t * data, uint32_t sadd)
{
    i2cBASE_t * i2cREGx=i2cREG1;
    
    if (I2C_NUM==1)
    {
        i2cREGx=i2cREG1;
    }
    else if (I2C_NUM==2)
    {
        i2cREGx=i2cREG2;
    }
    
    i2cSetSlaveAdd(i2cREGx, sadd);//设置从机地址
    i2cSetDirection(i2cREGx, I2C_RECEIVER);//设置传输方向：接收
    i2cSetCount(i2cREGx, length); //决定了接收多少个字节后发出STOP，这句必须有
    i2cSetMode(i2cREGx, I2C_MASTER);//设置模式：主机
    i2cSetStop(i2cREGx);//确保总线处于关闭状态
    i2cSetStart(i2cREGx);//发送开始信号
    i2cReceive(i2cREGx, length, data);//接收数据
    while(i2cIsBusBusy(i2cREGx) == true);
    while(i2cIsStopDetected(i2cREGx) == 0);
    i2cClearSCD(i2cREGx);//清除寄存器中的停止检测标志位
}

/** @fn void I2C_SetRepeatMode(i2cBASE_t *i2c, uint8_t mode)
*   @brief I2C 重复模式设置
*   @param[in] i2c    - 要使用的I2C端口 i2cREG1或i2cREG2
*   @param[in] mode   - 模式选择 重复模式：I2C_REPEATMODE_R   不重复模式：I2C_REPEATMODE_NR
*   @note 
*/
void I2C_SetRepeatMode(i2cBASE_t *i2c, uint8_t mode)
{
    if (mode==I2C_REPEATMODE_R)
    {
        i2c->MDR |= 128U;
    }
    else
    {
        i2c->MDR &= ~128U;
    } 
}
